package com.zzt.spider.service.spiderService;


import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zzt.spider.config.AppConfig;
import com.zzt.spider.model.entity.Episode;
import com.zzt.spider.model.entity.Series;
import com.zzt.spider.model.entity.SpiderSource;
import com.zzt.spider.model.mapper.EpisodeMapper;
import com.zzt.spider.model.mapper.SeriesMapper;
import com.zzt.spider.model.mapper.SpiderSourceMapper;
import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v114.network.Network;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class FreekanpianZFSpiderService {
    private static final Logger log = LoggerFactory.getLogger(FreekanpianZFSpiderService.class);
    @Autowired
    private EpisodeMapper episodeMapper;
    @Autowired
    private SeriesMapper seriesMapper;
    @Autowired
    private SpiderSourceMapper spiderSourceMapper;
    @Autowired
    private AppConfig appConfig;
    private static final int MAX_RETRIES = 3;
    private static final Duration DEFAULT_WAIT = Duration.ofSeconds(15L);

    public void startSpider(SpiderSource spiderSource) {
        log.info("组装头部");
        System.setProperty("webdriver.chrome.driver", this.appConfig.getChromedriver());
        ChromeOptions options = new ChromeOptions();
        options.addArguments(new String[]{"--remote-allow-origins=*", "--headless", "--disable-gpu", "--no-sandbox", "--disable-dev-shm-usage"});
        options.setBinary(this.appConfig.getBinary());
        log.info("开始try");
        ChromeDriver driver = null;

        try {
            driver = new ChromeDriver(options);
            DevTools devTools = driver.getDevTools();
            devTools.createSession();
            devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
            List<String> m3u8Urls = new ArrayList();
            devTools.addListener(Network.requestWillBeSent(), (request) -> {
                try {
                    String url = request.getRequest().getUrl();
                    if (url.endsWith(".m3u8")) {
                        log.info("\ud83c\udfaf 捕获到 m3u8: " + url);
                        String realUrl = extractRealM3u8Url(url);
                        if (realUrl != null) {
                            synchronized(m3u8Urls) {
                                if (!m3u8Urls.contains(realUrl)) {
                                    m3u8Urls.add(realUrl);
                                    log.info("真实 M3U8: " + realUrl);
                                }
                            }
                        }
                    }
                } catch (Exception var7) {
                    log.error("处理URL时出错: " + var7.getMessage());
                }

            });
            String baseUrl = spiderSource.getUrl();
            driver.get(baseUrl);
            WebDriverWait wait = new WebDriverWait(driver, DEFAULT_WAIT);
            List<WebElement> links = (List)wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector(".stui-content__thumb")));
            WebElement videoThumb = (WebElement)links.get(0);
            String imageUrl = videoThumb.findElement(By.tagName("img")).getAttribute("src");
            List<WebElement> messageLinks = (List)wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector(".stui-content__detail")));
            WebElement videoMessage = (WebElement)messageLinks.get(0);
            String title = videoMessage.findElement(By.cssSelector("h1.title")).getText();
            String year = videoMessage.findElement(By.cssSelector("a[href*='/year/']")).getText();
            String playUrl = videoMessage.findElement(By.cssSelector("a.btn.btn-primary")).getAttribute("href");
            String var10000 = this.appConfig.getSaveUrl();
            String currentOutputDir = var10000 + "_" + spiderSource.getId();
            File dir = new File(currentOutputDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            String uploadFIlePath = year + "/" + spiderSource.getId();

            try {
                downloadFile(imageUrl, currentOutputDir + "/cover_" + spiderSource.getId() + ".jpg");
            } catch (Exception var32) {
                log.error("❌ 下载封面图失败: " + var32.getMessage());
            }

            int i = 0;

            while(i < 3) {
                try {
                    String href = playUrl;
                    driver.get(href);
                    processEpisodeLinks(driver);
                    break;
                } catch (StaleElementReferenceException var34) {
                    log.info("\ud83d\udd04 元素过期，重试中... (" + (i + 1) + "/3)");
                    if (i == 2) {
                        log.info("❌ 达到最大重试次数，跳过此链接");
                    }

                    ++i;
                } catch (Exception var35) {
                    log.error("处理链接时出错: " + var35.getMessage());
                    break;
                }
            }

            if (!m3u8Urls.isEmpty()) {
                log.info("⏳ 开始下载捕获到的M3U8视频...");
                this.quit(driver);

                for(i = 0; i < m3u8Urls.size(); ++i) {
                    String currentM3u8 = (String)m3u8Urls.get(i);
                    log.info("\n\ud83d\udce5 正在处理第 " + (i + 1) + "/" + m3u8Urls.size() + " 个M3U8: " + currentM3u8);

                    try {
                        List<String> tsUrls = parseM3U8(currentM3u8);

                        for(int j = 0; j < tsUrls.size(); ++j) {
                            String tsUrl = (String)tsUrls.get(j);
                            String fileName = currentOutputDir + "/segment_" + j + ".ts";
                            downloadFile(tsUrl, fileName);
                            log.info("✅ 下载进度(%d/%d): %s%n", new Object[]{j + 1, tsUrls.size(), fileName});
                        }

                        this.uploadToR2(currentOutputDir, uploadFIlePath);
                    } catch (Exception var33) {
                        log.error("❌ 处理M3U8失败: " + var33.getMessage());
                        var33.printStackTrace();
                    }
                }

                boolean addFlag = seriesMapper.exists(new LambdaQueryWrapper<Series>().eq(Series::getCoverImageUrl, uploadFIlePath));
                if (!addFlag) {
                    Series series = new Series();
                    series.setSeriesName(title);
                    series.setYear(Integer.parseInt(year));
                    series.setUpdateTime(new Date());
                    series.setCoverImageUrl(uploadFIlePath);
                    series.setLocation(spiderSource.getLocation());
                    series.setType(4);
                    this.seriesMapper.insert(series);
                    Episode episode = new Episode();
                    episode.setEpisodeName("P1");
                    episode.setType(4);
                    episode.setSeriesId(series.getId());
                    episode.setUrl(uploadFIlePath);
                    episode.setCreateTime(new Date());
                    this.episodeMapper.insert(episode);
                }
                this.spiderSourceMapper.update(new LambdaUpdateWrapper<SpiderSource>().eq(SpiderSource::getId, spiderSource.getId()).set(SpiderSource::getState, 2));
            } else {
                log.info("⚠️ 没有捕获到任何m3u8地址");
            }
        } catch (Exception var36) {
            log.error("爬虫出错，id:"+spiderSource.getId());
            this.spiderSourceMapper.update(new LambdaUpdateWrapper<SpiderSource>().eq(SpiderSource::getId, spiderSource.getId()).set(SpiderSource::getState, 3));
            log.error("主程序出错: " + var36.getMessage());
            var36.printStackTrace();
        } finally {
            this.quit(driver);
        }

    }

    private void quit(ChromeDriver driver) {
        try {
            if (driver != null) {
                driver.quit();
                log.info("✅ ChromeDriver 已正常关闭");
            }
        } catch (Exception var4) {
            log.error("❌ ChromeDriver 关闭失败: " + var4.getMessage());
        }

        try {
            Runtime.getRuntime().exec("bash -c \"ps aux | grep '/mnt/spider/chrome-linux64/chrome' | grep -v grep | awk '{print $2}' | xargs -r kill -9\"");
            Runtime.getRuntime().exec("bash -c \"ps aux | grep 'chromedriver' | grep -v grep | awk '{print $2}' | xargs -r kill -9\"");
        } catch (IOException var3) {
            log.error("杀chrome失败: " + var3.getMessage());
        }

    }

    private static void processEpisodeLinks(WebDriver driver) {
        WebDriverWait wait = new WebDriverWait(driver, DEFAULT_WAIT);
        List<WebElement> episodeLinks = (List)wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.cssSelector("li a[title^='第']")));
        if (episodeLinks.isEmpty()) {
            log.info("⚠️ 没有找到任何播放链接");
        } else {
            for(int i = 0; i < Math.min(episodeLinks.size(), 3); ++i) {
                try {
                    List<WebElement> currentEpisodes = driver.findElements(By.cssSelector("li a[title^='第']"));
                    if (currentEpisodes.size() <= i) {
                        break;
                    }

                    WebElement ep = (WebElement)currentEpisodes.get(i);
                    String episodeHref = ep.getAttribute("href");
                    String episodeTitle = ep.getText();
                    log.info("\ud83d\udcfa " + episodeTitle + " 地址: " + episodeHref);
                    driver.get(episodeHref);

                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException var9) {
                        Thread.currentThread().interrupt();
                    }
                } catch (StaleElementReferenceException var10) {
                    log.info("\ud83d\udd04 剧集链接过期，跳过");
                } catch (Exception var11) {
                    log.error("处理剧集链接时出错: " + var11.getMessage());
                }
            }

        }
    }

    private static String extractRealM3u8Url(String url) {
        try {
            String decodedUrl = URLDecoder.decode(url, "UTF-8");
            if (decodedUrl.contains("url=")) {
                String[] parts = decodedUrl.split("url=");
                if (parts.length > 1) {
                    String realUrl = parts[1].split("&")[0];
                    return realUrl;
                }
            }

            return url;
        } catch (UnsupportedEncodingException var4) {
            log.error("URL解码失败: " + var4.getMessage());
            return null;
        } catch (Exception var5) {
            log.error("提取真实URL失败: " + var5.getMessage());
            return null;
        }
    }

    private static List<String> parseM3U8(String m3u8Url) throws IOException {
        List<String> tsUrls = new ArrayList();
        String m3u8Content = downloadContent(m3u8Url);
        if (!m3u8Content.contains("#EXT-X-ENDLIST")) {
            log.info("⚠️ 警告：检测到直播流，可能无法完整下载");
        }

        String baseUrl = m3u8Url.substring(0, m3u8Url.lastIndexOf("/") + 1);
        Pattern pattern = Pattern.compile("(?m)^[^#].*\\.ts(?:\\?.*)?$");

        String tsPath;
        for(Matcher matcher = pattern.matcher(m3u8Content); matcher.find(); tsUrls.add(tsPath)) {
            tsPath = matcher.group();
            if (!tsPath.startsWith("http")) {
                tsPath = baseUrl + tsPath;
            }
        }

        log.info("\ud83d\udd0d 共找到 " + tsUrls.size() + "个TS片段");
        return tsUrls;
    }

    private static String downloadContent(String fileUrl) throws IOException {
        HttpURLConnection conn = (HttpURLConnection)(new URL(fileUrl)).openConnection();
        conn.setRequestProperty("User-Agent", "Mozilla/5.0");
        conn.setConnectTimeout(10000);
        conn.setReadTimeout(30000);
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

        String var5;
        try {
            StringBuilder content = new StringBuilder();

            while(true) {
                String line;
                if ((line = reader.readLine()) == null) {
                    var5 = content.toString();
                    break;
                }

                content.append(line).append("\n");
            }
        } catch (Throwable var7) {
            try {
                reader.close();
            } catch (Throwable var6) {
                var7.addSuppressed(var6);
            }

            throw var7;
        }

        reader.close();
        return var5;
    }

    private static void downloadFile(String fileUrl, String outputPath) throws IOException {
        HttpURLConnection conn = (HttpURLConnection)(new URL(fileUrl)).openConnection();
        conn.setRequestProperty("User-Agent", "Mozilla/5.0");
        conn.setConnectTimeout(10000);
        conn.setReadTimeout(30000);
        InputStream in = conn.getInputStream();

        try {
            FileOutputStream out = new FileOutputStream(outputPath);

            try {
                byte[] buffer = new byte[8192];

                int bytesRead;
                while((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            } catch (Throwable var9) {
                try {
                    out.close();
                } catch (Throwable var8) {
                    var9.addSuppressed(var8);
                }

                throw var9;
            }

            out.close();
        } catch (Throwable var10) {
            if (in != null) {
                try {
                    in.close();
                } catch (Throwable var7) {
                    var10.addSuppressed(var7);
                }
            }

            throw var10;
        }

        if (in != null) {
            in.close();
        }

    }

    private void uploadToR2(String filePath, String uploadPath) {
        try {
            String scriptPath = this.appConfig.getUploadSh();
            ProcessBuilder pb = new ProcessBuilder(new String[]{"python3", scriptPath, filePath, uploadPath});
            pb.redirectErrorStream(true);
            Process process = pb.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line;
            while((line = reader.readLine()) != null) {
                log.info(line);
            }

            int exitCode = process.waitFor();
            log.info("上传脚本执行完毕，退出码：" + exitCode);
        } catch (InterruptedException | IOException var9) {
            var9.printStackTrace();
        }

    }
}
